李沐dl笔记
vgg
内存占用大,推理慢(深),但效果好
卷积层参数小,全连接层最 大问题是参数太大过拟合
所以最后一层全连接是很大的问题
大参数还有内存 bound 的问题
NiN
用卷积层替代全连接
两个 1*1 卷积无 padding, stride1 起全连接的作用(只做通道混合)
每个卷积后面跟两个全连接作为 NiN block
交替使用 NiN 块和 stride = 2 的 maxpooling 逐步减小高宽和增大通道数
最后使用全局平均池化得到输出(通道数 = 分类个数)
打印结构:
for layer in net:
X = layer(X)
print(layer.__class__.__name__, "output shape:\t", X.shape)
超级宽的 hidden layer: 非常容易过拟合
泛化性提高-> 收敛变慢
全连接的方案: 非常强, 收敛很快
GoogLeNet
inception 块: 不做选择, 全都要
output = output1 + o2 + o3 + o4
o1 = conv1x1
o2 = conv1x1 + conv3x3, padding 1
o3 = conv1x1 + conv3x3, padding 1 + conv5x5, padding 2
o4 = 3x3 maxpool, padding 1
四条路径从不同层面抽取信息, 在输出通道合并 concatenation
四条路径分配不同的通道数(你认为那种模式哪个通道的信息更重要)
降低通道数来控制模型复杂度
googlenet 5 段, 9 个 inception 块
不降低维数的 1x1 卷积就是通道融合
第一个 stage 总是把通道数拉上去, 高宽减下去, 先筛选出足够多的特征
v2: batch normalization
v3: 5x5-> 3x3, 5x5-> 1x7+7x1(单长和单宽)
v4: 残差连接
优点是模型参数少, 计算复杂度低
批量归一化
损失出现在最后, 后面的层训练快
反向传播: loss 在顶层, 数据在最底部, 底部的层(前面的层)训练慢, 底部层一变, 所有都得跟着变
导致离 loss 近的后面层需要重新学习多次, 导致收敛变慢
有没有方法让学习前面层的时候避免变化后面层?
批量归一化: 将分布固定, 来让输出模式稳定一些, 固定小批量的均值和方差
正则化, 将数据分布固定为 正态分布, 数据的修改只是在变化正态分布的超参数, 限制变化不要太剧烈
对于全连接, 作用 在激活函数前面, 作用在特征维度
对卷积, 作用在通道维
效果太好了, 原始论文觉得是减少内部协变量转移, 后续发现 可能就是等效于在每个小批量里面加入噪音来控制模型, 均值近似于随机偏移, 方差近似于随机缩放
因此没必要和丢弃混合使用
加速收敛(模式更稳定之后可以把 lr 调得更大), 但一般不改变模型精度
根据内存挑 batch size, 不能太大也不能太小, 然后调学习率和 epoch
ResNet
残差的重要性不必多言
深网络必有残差思想
新硬件
DSP 主要做数字计算处理长指令, FPGA 可编程阵列
工具链质量良莠不齐, 一次 "编译" 需要很久
AI ASIC: Google TPU eg
核心 systolic array, 专门做大矩阵乘法 2d 计算单元(PE)阵列, 卷积换成矩阵乘法
一般的矩阵乘: 切开和填充匹配 SA 大小
批量输入来降低延时, 其他硬件单元来处理别的 NN 操作子, 例如激活层
多卡并行
数据并行(切割小批量), 模型并行(切割模型, 适用于模型太大的时候),
all reduce: 将所有 gpu 的结果放到一个 gpu 上, 然后相加, 加完再复制回其他 gpu
nn.parallel.scatter
nn.DataParallel
多卡时也要相应的加大 batchsize 和 lr
大 batch size 在小模型上会采出重复样本导致浪费和一定程度上的过拟合
分布式
GPU 和 GPU 通信快, 和 CPU 通信慢, 和交换机网卡更慢
- 类似存储器山
解法是把 parameter server 尽量从 cpu 搬到 gpu 上
这样简单的 parameter 迁移分配就能在 gpu 本地完成, 不涉及到 cpu 的 copy(感觉像 DMA)
每个 worker 拿参数, 复制到 GPU 上, 每个 GPU 算自己的梯度, GPU 梯度求和, 传回服务器, 再更新, 回发
类似 mr, server mapper, 本地 gpu 完成计算和 combine, 在 server reduce
同步 SGD, 每个 worker 同步计算一个批量
所以需要调 batch size, 来针对并行省下的时间与通信开销做 trade off
实践:
- 大数据集
- 好的 GPU-GPU 和机器-机器带宽
- 高效的数据读取和预处理
- 好的计算(FLOP)和通信(model size)比 Inception > ResNet > AlexNet
- 足够大的 batch size
- 高效优化算法(因为 batch size 变大了, 如何适配)
- 更复杂的分布式有异步, 模型并行
一般 N 个类, batch size 差不多到 10N 再往上就不太能 fit 了
数据增广
已有数据集让他有更多多样性
- 在语言里面加背景噪音
- 改变图片的亮度, 颜色, 形状
一般的做法: 原始数据在线生成, 随机增强
测试不做增强
翻转:
- 左右翻转, 上下翻转
- 切割, 随即高宽比, 随机大小, 随机位置
其他:
- 高斯模糊
- 锐化
- 变形
- 滤镜
- 马赛克(相当于遮挡, 逼着去看全局)
- ...
从实际部署的场景反推需 要什么样的增强
异常检测, 偏斜数据, 重采样, 增广
mixup 增广: 有效但不知道为什么
torchvision.transforms
微调(迁移学习)
标注一个数据集很贵
希望在大数据集上做好的东西, 能以小代价迁移到小数据集上
神经网络分层两块: 特征提取+线性分类
dl: 让特征提取变得可以学习, 而不是人来提取特征
训练:
- 更强正则化
- 更小学习率
- 更少的数据迭代
源数据集远复杂于目标, 微调效果更好
固定一些层, 固定底部一些层的参数, 不参与更新
低层次的特征更加通用
小 trick, 微调的时候最后一层用大学习率, 前面用小的
迁移的也不能差太大, 否则效果很可能不够好
目标检测
bounding box
锚框: 提出多个被称为锚框的区域, 预测每个框里面是否有关注的物体, 如果是, 预测锚框到真实框的偏移
交并比 IoU
每个锚框是一个训练样本, 要么标注成背景, 要么关联一个真实边缘框
可能生成大量锚框, 导致大量的负类样本
选择合适的锚框(赋予锚框标号):
先生成一堆框, 之后算锚框 i 和真实框 j 的 IoU, 在 i, j 之中找最大的, 就得到了一组锚框和真实框的对应
然后从集合中剔除这个锚框 i 和边缘框 j(删除矩阵行列), 再找下一组
重复直到真实框为空, 这就是正类样本, 剩下的锚框挑一些作 为负类样本
锚框生成: 一种固定切分画格子
NMS 非极大抑制: 合并相似的预测框
- 选中非背景类的最大预测值
- 去掉所有和它 IoU 大于阈值的预测
- 重复直到所有预测要么被选中, 要么被去掉
生成锚框的另一种示例方法
宽度 , 高度
对给定几组 对每(n)个像素生成
算法的核心之一: 如何生成高质量锚框
锚框到偏移的算法: 多种多样
autogluon
工业界很少用模型融合和测试增强, 计算代价过高
通常固定模型超参数, 简单模型, 精力花在提升数据质量和加入的新数据
RCNN:
启发式搜索算法选择锚框
预训练模型对每个锚框抽取特征
训练一个 SVM 对类别分类
训练一个线性回归来预测偏移
RoI pooling
锚框均匀分割 mxn, 输出每块里面的最大值
不管锚框多大, 总是输出 mn
Fast RCNN
不再对每一个锚框抽取特征
而是将所有的锚框丢进 cnn(输入里面对应的映射区域), 一次 CNN 对整个图片抽取
Faster RCNN: 使用区域提议网络代替启发式搜索来获得更好的锚框
2-stage
Mask RCNN 如果有像素级别的编号, 给每个像素做预测, 用 FCN 利用信息
Faster RCNN: 速度非常慢, 精度高
SSD: single stage
基础网络抽特征, 多个 conv 减半高宽
每段都生成锚框
- 底部段拟合小物体, 顶部段拟合大物体
对每个锚框预测类别和边缘框
yolo: 追求快
ssd 锚框大量重叠, 浪费计算
均匀切分 SxS 个锚框, 每个锚框预测 B 个边缘框
后续 有许多微调和改进
工业常用
非锚框: 例如 central net
语义分割
像素级分类
应用: 背景虚化, 路面分割
另一个相近的概念: 实例分割
数据集: 输入是图片, label 也是图片(每个像素的值就是 label)
crop: 怎么做, 对输入进行裁剪, 在 label 上也要相对应的裁剪
拉伸也是需要特殊处理的
旋转? 一种是可以加一个 label 是旋转角度, 另一个是可以在转完的斜框上涨再画一个大框框住斜框
人像: 难点在光照, 阴影和背景
人像语义分割: pretrain model 已经很成熟
转置卷积
卷积的问题:不能很有效的增加高宽
类似语义分割这种-> 卷积不断减小高宽, 会影响像素级别的输出
增大输入高宽
为什么是转置卷积:
卷积等价于矩阵乘法 , 转置卷积就是
nn.ConvTranspose2d
卷积是下采样, 卷积是上采样
转置卷积与线性插值: 可以用线性插值作为转置卷积核的初始值
FCN
全连接卷积神经网络
用 dl 做语义分割的最早工作
用转置卷积替换 CNN 最后的全连接层+全局池化
- 先过 1x1 conv 压缩通道
- 再过转置卷积拉大图片, 得到像素级别的切分
- 思想是每个像素的的 label 信息这个 feature 应该存在 channels 里面
net = nn.Sequential(*list(pretrained_cnn.children()))[:-2]
可以用双线性插值的矩阵初始化转置卷积层的 kernel
loss: 由于每一个像素都有了 label
所以在矩阵上做均值再 cross_entropy
样式迁移
基于 CNN 的样式迁移
核心思想: 训练一个 CNN, 将他作用在内容图片上得到输出, 在样式图片上得到输出
而输出图片在内容层上的输出和内容图片在内容层上的输出相近(content loss)
输出图片在样式层上的输出和样式图片在样式层上的输出相近(style loss)
训练的不是 CNN, 而是输入网络的的“输出图片”